import { message, Modal } from "ant-design-vue";
import { transferDicts } from "../dicts/scoreToneTransfer";

// 音阶比值
const typeRate = {
  "whole": 1,
  "half": 1 / 2,
  "quarter": 1 / 4,
  "eighth": 1 / 8,
  "16th": 1 / 16,
  "32nd": 1 / 32,
}

// note方法
export const noteUtils = {
  isChord: (note) => {
    return note?.getElementsByTagName?.("chord")?.length > 0;
  },
  isRest: (note) => {
    return note?.getElementsByTagName?.("rest")?.length > 0;
  },
  isTiedStart: (note) => {
    const tied = note?.getElementsByTagName?.("tied")?.[0];
    return tied?.getAttribute?.("type") === 'start';
  },
  isTiedStop: (note) => {
    const tied = note?.getElementsByTagName?.("tied")?.[0];
    return tied?.getAttribute?.("type") === 'stop';
  },
  isGrace: (note) => {
    return note?.getElementsByTagName?.("grace")?.length > 0;
  },
  hasDot: (note) => {
    return note?.getElementsByTagName?.("dot")?.length > 0;
  },
  getTimeModification: (note) => {
    let count = 1;
    const timeModification = note?.getElementsByTagName?.("time-modification")?.[0];
    if (timeModification) {
      const actualNotes = timeModification?.getElementsByTagName?.("actual-notes")?.[0]?.textContent;
      const normalNotes = timeModification?.getElementsByTagName?.("normal-notes")?.[0]?.textContent;
      if (actualNotes && normalNotes) {
        count = normalNotes / actualNotes;
      }
    }
    return count;
  }
}

// 状态池
let state = {
  // 重唱次数，第一次唱时是1
  nowCount: 1,
  repeatForword: {
    measureIdx: 0,
    itemIdx: 0,
  },
  // 重唱标记点
  repeatDot: [] as any[],
  // endingNumber
  endingNumber: "0",
  endingState: '',
  errors: [] as string[],
  tempChords: [] as string[],
}

//初始化状态池
const initState = () => {
  state.nowCount = 1;
  state.repeatForword = {
    measureIdx: 0,
    itemIdx: 0,
  };
  state.repeatDot = [];
  state.endingNumber = "0";
  state.endingState = '';
  state.errors = [];
  state.tempChords = [];
}

// 获取小节内和弦
const getChords = (measure) => {
  let arr: string[] = [];
  const harmonys = measure?.getElementsByTagName?.("harmony");
  try {
    Array?.prototype?.forEach?.call?.(harmonys, x => {
      let str = "";
      const root = x?.getElementsByTagName?.("root")?.[0];
      const rootStep = root?.getElementsByTagName?.("root-step")?.[0]?.textContent;
      rootStep && (str += rootStep);
      const rootAlter = root?.getElementsByTagName?.("root-alter")?.[0]?.textContent;
      if (rootAlter === '-1') {
        str += "b";
      } else if (rootAlter === '1') {
        str += "#";
      }
      const kind = x?.getElementsByTagName?.("kind")?.[0];
      const kindText = kind?.getAttribute?.("text");
      // kindText && (str += kindText);s
      if (kindText) {
        str += kindText;
      } else {
        kind?.textContent.startsWith('minor') && (str += 'm');
      }
      const bass = x?.getElementsByTagName?.("bass")?.[0];
      bass && (str += '/');
      const bassStep = bass?.getElementsByTagName?.("bass-step")?.[0]?.textContent;
      bassStep && (str += bassStep);
      const bassAlter = bass?.getElementsByTagName?.("bass-alter")?.[0]?.textContent;
      if (bassAlter === '-1') {
        str += "b";
      } else if (bassAlter === '1') {
        str += "#";
      }
      arr.push(str);
    })
  } catch (err) { }
  return arr;
}

// 判断当前为小节中间note，960的位置
const reduceDurationIsHalf = (measure) => {
  let idx = -1;
  const multiple = getXmlMultiple(measure);
  let durations = 0;
  const childs = measure.childNodes;
  childs.forEach((item, index) => {
    if (item.nodeName === 'note') {
      if (durations === 960) {
        idx = index;
      }
      const duration = item?.getElementsByTagName?.("duration")?.[0]?.textContent || 0;
      // 多音
      if (noteUtils.isChord(item)) {
        durations -= Number(duration) * multiple;
      }
      durations += Number(duration) * multiple;
    }
  })
  return idx;
}

// measure子项基类
class measureItemBase {
  model: any;
  constructor(model) {
    this.model = model;
  }
  // 处理数据(默认不处理)
  dealData(measuresData, model) { }
  async next(callback) {
    return new Promise((resolve) => {
      const measuresLen = this.model.measures.length;
      const nowMeasure = this.model.measures[this.model.measureIdx];
      const itemLen = nowMeasure.childNodes.length;
      if (this.model.itemIdx < itemLen - 1) {
        this.model.itemIdx++;
      } else {
        this.model.measureIdx++;
        this.model.itemIdx = 0;
        // 当前索引
        this.model.measureNowIdx++;
      }
      setTimeout(async () => {
        if (this.model.measureIdx <= measuresLen - 1) {
          await callback(this.model.measureIdx, this.model.itemIdx, this.model.measureNowIdx);
        }
        resolve(true);
      }, 10)
    })
  }
}
// measure子项--barline
class measureItemBarline extends measureItemBase {
  dealData(measuresData, model) {
    const childs = model?.measures?.[model.measureIdx]?.childNodes;
    const barline = childs?.[model.itemIdx];
    // 重唱标签
    const repeat = barline?.getElementsByTagName?.("repeat")?.[0];
    if (repeat) {
      // 重唱开始标签
      if (repeat.getAttribute?.("direction") === "forward") {
        state.repeatForword.measureIdx = model.measureIdx;
        state.repeatForword.itemIdx = model.itemIdx;
      }
    }
    // 分段ending
    const ending = barline?.getElementsByTagName?.("ending")?.[0];
    const endingNumber = ending?.getAttribute?.("number");
    const endingState = ending?.getAttribute?.("type");
    if (endingNumber) {
      state.endingNumber = endingNumber;
    }
    if (endingState) {
      state.endingState = endingState || "";
    }
    if (Number(endingNumber) > 1 && endingState === "stop") {
      state.endingNumber = "0";
      state.endingState = "";
    }
  }
  async next(callback) {
    return new Promise(async (resolve) => {
      const childs = this.model?.measures?.[this.model.measureIdx]?.childNodes;
      const barline = childs?.[this.model.itemIdx];
      // 重唱标签
      const repeat = barline?.getElementsByTagName?.("repeat")?.[0];
      // 遇到重唱标记，并且该重唱标记未使用时
      if (repeat?.getAttribute?.("direction") === "backward" && !state.repeatDot.some(x => x.measureIdx === this.model.measureIdx && x.itemIdx === this.model.itemIdx)) {
        const repeatMeasureIdx = state.repeatForword.measureIdx;
        const repeatItemIdx = state.repeatForword.itemIdx;
        state.repeatDot.push({
          measureIdx: this.model.measureIdx,
          itemIdx: this.model.itemIdx
        });
        state.endingState = "";
        this.model.measureIdx = repeatMeasureIdx;
        this.model.itemIdx = repeatItemIdx + 1;
        state.nowCount++;
        this.model.measureNowIdx++;
        await callback(this.model.measureIdx, this.model.itemIdx, this.model.measureNowIdx);
        resolve(true);
        return;
      }
      await super.next(callback);
      resolve(true);
    })
  }
}
// measure子项--note
class measureItemNote extends measureItemBase {
  // 获取初始和弦
  getChooseOri(e) {
    if (e.indexOf("E#") > -1) {
      e = e.replaceAll("E#", "F");
    }
    if (e.indexOf("Fb") > -1) {
      e = e.replaceAll("Fb", "E");
    }
    if (e.indexOf("B#") > -1) {
      e = e.replaceAll("B#", "C");
    }
    if (e.indexOf("Cb") > -1) {
      e = e.replaceAll("Cb", "B");
    }
    return e;
  }
  dealData(measuresData, model) {
    // 分段ending不符合时不执行操作
    if ((state.endingState === 'start' || state.endingState === 'discontinue') && state.endingNumber !== state.nowCount.toString()) {
      return;
    }
    const childs = model?.measures?.[model.measureIdx]?.childNodes;
    const note = childs?.[model.itemIdx];
    if (noteUtils.isGrace(note)) {
      return;
    }
    const staff = note?.getElementsByTagName?.("staff")?.[0]?.textContent;
    let bol = false;
    if (model.hasP2) {
      bol = staff === undefined;
    } else {
      if (model.hasStaff1) {
        bol = staff === "1";
      } else {
        bol = staff === undefined;
      }
    }
    if (bol) {
      let lyric: any = {};
      const lyricDom = note?.getElementsByTagName?.("lyric");
      let lyrics = Array.prototype.filter.call(lyricDom, x => x?.getAttribute?.("number") === state.nowCount.toString());
      let lyrics2 = Array.prototype.filter.call(lyricDom, x => x?.getAttribute?.("number") === "1");
      // n不是第一遍，第n遍时没有歌词,没有进入ending分段中,n与ending分段不一致时需要判断
      if (state.nowCount > 1 && lyrics.length === 0 && (state.endingState === '' && state.nowCount !== Number(state.endingNumber))) {
        lyric.repeatWord = 1;
      }
      let tLyrics = lyrics.length > 0 ? lyrics : lyrics2;
      const word = tLyrics?.[0]?.getElementsByTagName?.("text")?.[0]?.textContent || "";
      if (word === '\\' || word === '/') {
        lyric.breakLine = 1;
        lyric.word = "";
      } else if (word.indexOf('\\') > -1) {
        lyric.breakLine = 1;
        lyric.word = word.replaceAll("\\", "");
      } else if (word.indexOf('/') > -1) {
        lyric.breakLine = 1;
        lyric.word = word.replaceAll("/", "");
      } else {
        lyric.word = word;
      }
      lyric.type = note?.getElementsByTagName?.("type")?.[0]?.textContent || "whole";
      lyric.duration = note?.getElementsByTagName?.("duration")?.[0]?.textContent || "";
      lyric.measureIdx = model.measureNowIdx;
      lyric.isRest = noteUtils.isRest(note);
      lyric.isChord = noteUtils.isChord(note);
      lyric.isGrace = noteUtils.isGrace(note);
      lyric.isTiedStart = noteUtils.isTiedStart(note);
      lyric.isTiedStop = noteUtils.isTiedStop(note);
      lyric.hasDot = noteUtils.hasDot(note);
      lyric.timeModification = noteUtils.getTimeModification(note);
      // 获取当前小节拥有的和弦
      let chords = getChords(model?.measures?.[model.measureIdx]);
      // 当前小节没有和弦使用上一小节的和弦
      if (chords.length === 0) {
        chords = state.tempChords;
        chords = chords[chords.length - 1] ? [chords[chords.length - 1]] : [];
      }
      // 仍然没有和弦，报错
      if (chords.length === 0) {
        const err = `第${model.measureIdx + 1}小节缺少和弦`;
        state.errors.includes(err) || state.errors.push(err);
      };
      state.tempChords = chords;
      // 和弦数不能大于2
      if (chords.length > 2) {
        const err = `和弦数不得大于2,第${model.measureIdx + 1}小节存在${chords.length}个和弦`;
        state.errors.includes(err) || state.errors.push(err);
      };
      if (measuresData.findIndex(x => x.measureIdx === model.measureNowIdx) === -1) {
        if (chords.length > 0) {
          lyric.chooseOri = this.getChooseOri(chords[0]);
          lyric.beat = (1920 / chords.length).toFixed(0);
        }
      }
      if (chords.length === 2) {
        const halfIdx = reduceDurationIsHalf(model?.measures?.[model.measureIdx]);
        if (halfIdx < 0) {
          const err = `第${model.measureIdx + 1}小节存在2个和弦,但第二个和弦没有可标记的位置`;
          state.errors.includes(err) || state.errors.push(err);
        }
        if (halfIdx === model.itemIdx) {
          lyric.chooseOri = this.getChooseOri(chords[1]);
          lyric.beat = (1920 / chords.length).toFixed(0);
        }
      }
      measuresData.push(lyric);
    }
  }
}
// measure子项工厂类
class MeasureItemFactory {
  createMeasureItem(type, model) {
    switch (type) {
      case "note":
        return new measureItemNote(model);
      case "barline":
        return new measureItemBarline(model);
      default:
        return new measureItemBase(model);
    }
  }
}

class MeasuresClass {
  measures;
  measuresData = [];
  measureNowIdx = 0;
  measureIdx = 0;
  itemIdx = 0;
  hasP2 = false;
  hasStaff1 = false;
  constructor(obj) {
    this.hasP2 = obj.hasP2;
    this.hasStaff1 = obj.hasStaff1;
  }
  // 获取小节
  getMeasures(part) {
    return part.getElementsByTagName("measure");
  }
  // 填入小节
  setMeasures(measures) {
    this.measures = measures;
  }
  // 遍历
  async traverse() {
    await this.dealNowMeasureItem();
  }
  // 处理当前索引measure子项
  async dealNowMeasureItem(measureIdx = undefined, itemIdx = undefined, measureNowIdx = undefined) {
    measureIdx !== undefined && (this.measureIdx = measureIdx);
    itemIdx !== undefined && (this.itemIdx = itemIdx);
    measureNowIdx !== undefined && (this.measureNowIdx = measureNowIdx);
    //获取当前索引measure
    const nowMeasure = this.measures[this.measureIdx];
    //获取当前索引measure子项
    const nowItem = nowMeasure.childNodes[this.itemIdx];
    // 当前子项标签名
    const nowItemName = nowItem.nodeName;
    // 当前子项类
    const measureItemFactory = new MeasureItemFactory();
    const measureItem = measureItemFactory.createMeasureItem(nowItemName, this);
    // 执行当前子项数据操作
    measureItem?.dealData?.(this.measuresData, this);

    // 执行最后一步，next方法
    await measureItem.next(this.dealNowMeasureItem.bind(this));
  }
  // 获取数据
  getData() {
    let arr: any[] = [];
    for (let i = 0; i <= this.measureNowIdx; i++) {
      let arr2: any[] = [];
      this.measuresData.forEach((x: any) => {
        if (x.measureIdx === i) {
          arr2.push(x);
        }
      })
      if (arr2.length > 0) {
        arr.push(arr2);
      }
    }
    return arr;
  }
}
export default class museXmlDealClass {
  part;
  hasP2;
  hasStaff1;
  constructor(xml) {
    this.part = xml.getElementsByTagName("part")[0];
    this.hasP2 = xml.getElementsByTagName("part").length > 1;

    this.hasStaff1 = (() => {
      let bol = false;
      const part = xml.getElementsByTagName("part")[0];
      const staffs = part?.getElementsByTagName?.("staff") || [];
      Array.prototype.forEach.call(staffs, x => {
        if (x?.textContent === '1') {
          bol = true;
        }
      })
      return bol;
    })()
  }
  // 检查多声道
  inspectVoice() {
    let bol = false;
    const voices = this.part?.getElementsByTagName?.("voice") || [];
    Array.prototype.forEach.call(voices, x => {
      if (x?.textContent === '2') {
        bol = true;
      }
    })
    return bol;
  }
  // 检查拍号
  inspectBeat() {
    let error: string | null = null;
    try {
      const attributes = this.part?.getElementsByTagName?.("attributes");
      Array.prototype.forEach.call(attributes, x => {
        const time = x?.getElementsByTagName?.("time")?.[0];
        const beatType = time?.getElementsByTagName?.("beat-type")?.[0]?.textContent;
        const beats = time?.getElementsByTagName?.("beats")?.[0]?.textContent;
        if (beatType && beats) {
          if (!(Number(beatType) === 4 && [2, 4].includes(Number(beats)))) {
            error = `暂不支持解析${Number(beatType)}${Number(beats)}拍曲谱!`;
            throw "停止循环";
          }
        }
      })
    } catch (err) { }
    return error;
  }
  // 检查倚音
  inspectGrace() {
    let error: string | null = null;
    try {
      const grace = this.part?.getElementsByTagName?.("grace");
      if (grace?.length > 0) {
        error = `暂不支持特殊曲谱,请联系技术人员!`;
      }
    } catch (err) { }
    return error;
  }
  //检查震音
  inspectTremolo() {
    let error: string | null = null;
    try {
      const notations = this.part?.getElementsByTagName?.("notations");
      Array.prototype.forEach.call(notations, x => {
        const ornaments = x?.getElementsByTagName?.("ornaments")?.[0];
        const tremolo = x?.getElementsByTagName?.("ornaments")?.[0];
        if (tremolo) {
          error = `暂不支持特殊曲谱,请联系技术人员!`;
        }
      })
    } catch (err) { }
    return error;
  }
  // 检查未定义符号
  inspectUndefined() {
    let error: string | null = null;
    try {

    } catch (err) { }
    return error;
  }
  // 检查曲谱
  inspect() {
    if (this.inspectVoice()) return "不支持处理多声道曲谱!";
    if (this.inspectBeat()) return this.inspectBeat();
    if (this.inspectGrace()) return this.inspectGrace();
    if (this.inspectTremolo()) return this.inspectTremolo();
    // if (this.inspectUndefined()) return this.inspectUndefined();
    return null;
  }
  // 初始化 
  init() {
    initState();
  }
  async deal() {
    this.init();
    const error = this.inspect();
    if (error) {
      message.error(error);
      return;
    }
    const measureInstance = new MeasuresClass({ hasP2: this.hasP2, hasStaff1: this.hasStaff1 });
    // 获取所有小节
    const measures = await measureInstance.getMeasures(this.part);
    // 填入所有小节
    await measureInstance.setMeasures(measures);
    const hide = message.loading("xml文件解析中...", 0);
    // 遍历小节内容
    await measureInstance.traverse();
    const measuresData = measureInstance.getData();
    // 曲谱调号
    const scoreToneData = await this.getScoreTone();
    hide();
    if (state.errors.length > 0) {
      Modal.confirm({
        title: "错误提示",
        content: <div>
          {
            state.errors.map(x => (
              <div style={{ color: "red" }}>{x}</div>
            ))
          }
        </div>,
      })
    }
    return {
      measures: measuresData,
      score_tone: scoreToneData,
    }
  }
  // 获取曲谱调号
  async getScoreTone() {
    const measure_first = this.part.getElementsByTagName("measure")[0];
    const attributes = measure_first.getElementsByTagName("attributes")[0];
    const key = attributes.getElementsByTagName("key")[0];
    const fifths = key.getElementsByTagName("fifths")[0];
    return transferDicts[fifths.textContent.toString()];
  }
  // 获取重唱结束标识数量
  getBackwordCount(part) {
    let count = 0;
    const barline = part.getElementsByTagName("barline");
    Array.prototype.forEach.call(barline, x => {
      const repeat = x.getElementsByTagName("repeat")[0];
      if (repeat?.getAttribute?.("direction") === "backward") {
        count++;
      }
    })
    return count;
  }
  // 获取重唱开始标识数量
  getForwardCount(part) {
    let count = 0;
    const barline = part.getElementsByTagName("barline");
    Array.prototype.forEach.call(barline, x => {
      const repeat = x.getElementsByTagName("repeat")[0];
      if (repeat?.getAttribute?.("direction") === "forward") {
        count++;
      }
    })
    return count;
  }
}

export const getXmlMultiple = (measure) => {
  const note = measure?.getElementsByTagName?.("note")?.[0];
  const duration = note?.getElementsByTagName?.("duration")?.[0]?.textContent;
  const type = note?.getElementsByTagName?.("type")?.[0]?.textContent || "whole";
  const hasDot = note?.getElementsByTagName?.("dot")?.[0];
  const type_count = typeRate[type];
  const timeModification = noteUtils.getTimeModification(note);
  return Number((1920 * (hasDot ? type_count * 1.5 * timeModification : type_count * timeModification) / duration));
}

export const getMultiple = (measure) => {
  const note = measure[0]?.isGrace ? measure[1] : measure[0];
  const duration = note?.duration;
  const type = note?.type || "whole";
  const hasDot = note?.hasDot;
  const type_count = typeRate[type];
  const timeModification = note?.timeModification;
  return Number((1920 * (hasDot ? type_count * 1.5 * timeModification : type_count * timeModification) / duration));
}